﻿#region Class Header
/* ========================================================================
* 【本类功能概述】
* 
* 作者：chinesedragon 
* 时间：5/2/2013 10:24:29 AM
* 文件名：UploadHelper
* 版本：V1.0.1
*
* 修改时间： 
* 修改说明：
* ========================================================================
*/
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;

namespace Publisher
{
    public class UploadHelperFactory
    {
        public static UploadHelper CreateInstance(int ntype, string url, string user="", string pwd="")
        {
            switch (ntype)
            {
                default:
                case 0:
                    return null;
                case 1:
                    return new UploadHelperForLocal();
                case 2:
                    return new UploadHelperForHttp();
                case 3:
                    return new UploadHelperForFtp(user, pwd);
                case 4:
                    return new UploadHelperForWin32Share(url, user, pwd);
            }
        }
    }

    public abstract class UploadHelper : IDisposable
    {
        public abstract void UploadFile(string sourceDir, string destDir, string fileName,ref long uploadSize, BackgroundWorker worker= null, long totalSize=0);

        public abstract void Dispose();
    }

    public class UploadHelperForLocal : UploadHelper
    {
        public override void Dispose()
        {
            ;
        }
        public override void UploadFile(string sourceDir, string destDir, string fileName, 
            ref long uploadSize, BackgroundWorker worker = null, long totalSize = 0)
        {
            string local = sourceDir + "\\" + fileName;
            string dest = destDir + "\\" + fileName;
            UploadFile(local, dest, ref uploadSize, worker, totalSize);
        }

        private void UploadFile(string sourceFile, string DestFile, ref long uploadSize, BackgroundWorker worker, long totalSize)
        {
            string dir = Path.GetDirectoryName(DestFile);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            File.Copy(sourceFile, DestFile, true);
            FileInfo fi = new FileInfo(DestFile);

            uploadSize += fi.Length;

            var progress = Math.Min(uploadSize, totalSize);
            var percentage = (int)((Math.Round(Convert.ToDecimal(progress) / Convert.ToDecimal(totalSize) * 1.0M, 2)) * 100);
            worker.ReportProgress(percentage);
        }
    }

    public class UploadHelperForHttp: UploadHelper
    {
        public override void Dispose()
        {
            ;
        }
        public override void UploadFile(string sourceDir, string destDir, string fileName,
            ref long uploadSize, BackgroundWorker worker = null, long totalSize = 0)
        {
            ;//todo 实现HTTP上传方式
        }
    }

    public class UploadHelperForFtp : UploadHelper
    {
        private string userName = string.Empty;
        private string userPassword = string.Empty;

        public UploadHelperForFtp(string user, string pwd)
        {
            userName = user;
            userPassword = pwd;
        }

        public override void Dispose()
        {
            ;
        }
        public override void UploadFile(string sourceDir, string destDir, string fileName,
            ref long uploadSize, BackgroundWorker worker = null, long totalSize = 0)
        {
            string local = sourceDir + "\\" + fileName;
            local = local.Replace("\\\\", "\\");
            string dest = destDir + "/" + fileName;
            dest = dest.Replace("\\", "/")/*.Replace("//", "/")*/;
            /*dest = dest.ToLower().Replace("ftp:/", "ftp://");*/
            UploadFile(local, dest, ref uploadSize, worker, totalSize);
        }

        private void UploadFile(string sourceFile, string DestFile, ref long uploadSize, BackgroundWorker worker, long totalSize)
        {
            string pFtpPath = DestFile.Substring(0, DestFile.LastIndexOf("/"));
            if (pFtpPath.LastIndexOf("/") > 5)
            {
                string pFtpParentPath = pFtpPath.Substring(0, pFtpPath.LastIndexOf("/"));
                MakeFtpFolder(pFtpParentPath);
            }
            MakeFtpFolder(pFtpPath);

            FtpWebRequest request = WebRequest.Create(DestFile) as FtpWebRequest;
            request.Credentials = new NetworkCredential(userName, userPassword);
            request.Method = WebRequestMethods.Ftp.UploadFile;

            using (FileStream fs = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
            {
                using (Stream s = request.GetRequestStream())
                {
                    int bufferlen = 1024*2;
                    byte[] upBuffer = new byte[bufferlen];
                    int nRead = 0;
                    while ((nRead = fs.Read(upBuffer, 0, bufferlen)) > 0)
                    {
                        s.Write(upBuffer, 0, nRead);

                        uploadSize += nRead;

                        var progress = Math.Min(uploadSize, totalSize);
                        var percentage = (int)((Math.Round(Convert.ToDecimal(progress) / Convert.ToDecimal(totalSize) * 1.0M, 2)) * 100);
                        worker.ReportProgress(percentage);
                    }
                }
            }
        }

        private void MakeFtpFolder(string ftpPath)
        {
            FtpWebRequest request = WebRequest.Create(ftpPath) as FtpWebRequest;
            request.Credentials = new NetworkCredential(userName, userPassword);
            request.Method = WebRequestMethods.Ftp.MakeDirectory;
            try
            {
                FtpWebResponse response = request.GetResponse() as FtpWebResponse;
            }
            catch
            {
                request.Abort();
            }
        }
    }

    public class UploadHelperForWin32Share : UploadHelper
    {
        internal class Win32Share : IDisposable
        {
            [DllImport("advapi32.dll", SetLastError=true)]
            static extern bool LogonUser(string username, string domain, string password,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            static extern bool CloseHandle(IntPtr handle);
            [DllImport("advapi32.dll")]
            static extern bool ImpersonateLoggedOnUser(IntPtr hToken);
            [DllImport("advapi32.dll")]
            static extern bool RevertToSelf();

            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_NEWCREDENTIALS = 9;
            private bool bDisposed = false;

            public Win32Share(string domain, string username, string password)
            {
                IntPtr pExistingTokenHandle = new IntPtr(0);
                IntPtr pDuplicateTokenHandle = new IntPtr(0);

                try
                {
                    // get handle to token
                    bool bImpresonated = LogonUser(username, domain, password,
                        LOGON32_LOGON_NEWCREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);
                    if (bImpresonated)
                    {
                        if (!ImpersonateLoggedOnUser(pExistingTokenHandle))
                        {
                            int nErrorCode = Marshal.GetLastWin32Error();
                            throw new Exception("ImpersonateLoggedOnUser error;Code=" + nErrorCode);
                        }
                    }
                    else
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();
                        throw new Exception("LogonUser error;Code=" + nErrorCode);
                    }
                }
                finally
                {
                    //close handle(s)
                    if (pExistingTokenHandle != IntPtr.Zero)
                        CloseHandle(pExistingTokenHandle);
                    if (pDuplicateTokenHandle != IntPtr.Zero)
                        CloseHandle(pDuplicateTokenHandle);
                }
            }

            protected virtual void Dispose(bool disposing)
            {
                if (!bDisposed)
                {
                    RevertToSelf();
                    bDisposed = true;
                }
            }

            public void Dispose()
            {
                Dispose(true);
            }
        }

        private Win32Share w32Share = null;
        public UploadHelperForWin32Share(string domain, string user, string pwd)
        {
            w32Share = new Win32Share(domain, user, pwd);
        }

        public override void Dispose()
        {
            if (w32Share != null)
            {
                w32Share.Dispose();
            }
        }

        public override void UploadFile(string sourceDir, string destDir, string fileName,
            ref long uploadSize, BackgroundWorker worker = null, long totalSize = 0)
        {
            string local = sourceDir + "\\" + fileName;
            string dest = destDir + "\\" + fileName;
            UploadFile(local, dest, ref uploadSize, worker, totalSize);
        }

        private void UploadFile(string sourceFile, string DestFile, ref long uploadSize, BackgroundWorker worker, long totalSize)
        {
            string dir = Path.GetDirectoryName(DestFile);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            File.Copy(sourceFile, DestFile, true);
            FileInfo fi = new FileInfo(DestFile);

            uploadSize += fi.Length;

            var progress = Math.Min(uploadSize, totalSize);
            var percentage = (int)((Math.Round(Convert.ToDecimal(progress) / Convert.ToDecimal(totalSize) * 1.0M, 2)) * 100);
            worker.ReportProgress(percentage);
        }
    }
}
